iT邦幫忙

2022 iThome 鐵人賽

DAY 24
0

slot的概念

在內層的元件中開一個缺口給外層的元件使用,所以外部的元件可以直接操作內層元件的HTML結構。

我們可以透過 slot 從外層直接操作內層的HTML結構。

Slot 插巢與插巢預設值

在需要設置插巢的地方加上 <slot> 標籤。

<card>
    <p>這是由外層定義的</p>
  </card>
app.component('card', {
    template: `<div class="card" style="width: 18rem;">
      <div class="card-header">
        元件 Header
      </div>
      <div class="card-body">
      //在這裡加入 <slot> 標籤
        <slot></slot>
      </div>
      <div class="card-footer">
        元件 Footer
      </div>
    </div>`
  })

在開發過程中,如果我們開放插巢的話,建議提供預設值。 當我們外層有內容時就使用外層的,外層沒內容就使用預設值。

而預設值直接在 slot 內插入內容即可。

<card></card>
  app.component('card', {
    template: `<div class="card" style="width: 18rem;">
      <div class="card-header">
        元件 Header
      </div>
      <div class="card-body">
        <slot><p>這是預設值</p></slot>
      </div>
      <div class="card-footer">
        元件 Footer
      </div>
    </div>`
  })

具名插巢

如果我們希望能夠開放多個插巢呢?

我們可以直接開三個 slot。 但要特別注意,當我們開三個 slot,但沒有 特別給予名稱 的話,外層就不知道哪個結構是對應哪個插巢。

因此我們可以在 slot上加入 name=""

  app.component('card2', {
    template: `<div class="card" style="width: 18rem;">
      <div class="card-header">
        <slot name="header">元件 Header</slot>
      </div>
      <div class="card-body">
        <slot>這段是預設的文字</slot>
      </div>
      <div class="card-footer">
        <slot name="footer">元件 Footer</slot>
      </div>
    </div>`
  });

並在HTML結構上使用 template標籤並加上 v-slot=" ",預設內容則使用 v-slot:default

  <card2>
    <template v-slot:header>我喜歡這張卡片</template>
    <!--  預設請加入 default  -->
    <template v-slot:default>這是卡片 2 號</template>
    <template v-slot:footer>這是卡片腳</template>
  </card2>

具名插巢縮寫

具名插巢也有縮寫,v-slot可以直接縮寫成 #,會得到一樣的結果。

<card2>
    <template #header>我喜歡這張卡片</template>
    <!--  預設請加入 default  -->
    <template #default>這是卡片 2 號</template>
    <template #footer>這是卡片腳</template>
  </card2>

插巢 Prop

在元件的概念裡,外層和內層元件的資料狀態是分離的,兩個資料狀態各自獨立,因此在插巢區塊,一樣不能取得內元件的資料狀態。

我們可以透過將元件插巢中的部分資料狀態,透過 props 傳送給外層做使用,這也稱為 slot props

  1. 先用v-slot:default對應插巢位置
  2. 在其中設置 product 的 props
  3. v-slot:default之後賦予值的名稱
<card>
    <!-- 取值的名稱 -->
    <template v-slot:default ="slotProps">
      我想取出元件的值來使用
      {{slotProps}}
      <br><br>
      {{slotProps.product.name}}
    </template>
  </card>
app.component('card', {
    data() {
      return {
        product: {
          name: '蛋餅',
          price: 30,
          vegan: false
        },
      }
    },
    template: `<div class="card" style="width: 18rem;">
      <div class="card-body" >
        <slot :product="product"></slot>
      </div>
    </div>`
  });

那如果我們要從slotProps中取出多個值呢?(解構)

  1. 先用 props 把外層的 product 資料傳進來
  2. 再把 :product="product" :veganName="veganName 傳出來
  3. v-slot:default之後賦予一個物件 : {product,veganName}
<card2 :product="product">
    <template #header>
      買早餐
    </template>
    <template #default ="{product,veganName}">
      {{ product }}
      {{ veganName }}
    </template>
  </card2>
app.component('card2', {
    props: ['product'],
    data() {
      return {
        veganName: ''
      }
    },
    created() {
      this.veganName = this.product.vegan ? '素食' : '非素食';
    },
    template: `<div class="card" style="width: 18rem;">
      <div class="card-body" >
        <slot :product="product" :veganName="veganName"></slot>
      </div>
    </div>`
  })

預設值

假如我們少傳出:veganName="veganName",我們也可以設定預設值。

<card2 :product="product">
    <template #header>
      買早餐
    </template>
    <template #default ="{product,veganName = '是素非素'}">
      {{ product }}
      {{ veganName }}
    </template>
  </card2>
  app.component('card2', {
    props: ['product'],
    data() {
      return {
        veganName: ''
      }
    },
    created() {
      this.veganName = this.product.vegan ? '素食' : '非素食';
    },
    template: `<div class="card" style="width: 18rem;">
      <div class="card-body" >
        <slot :product="product" ></slot>
      </div>
    </div>`
  })

JS

<script type="module">
  const app = Vue.createApp({
    data() {
      return {
        product: {
          name: '蛋餅',
          price: 30,
          vegan: false
        }
      }
    },
  });

  app.component('card', {
    data() {
      return {
        product: {
          name: '蛋餅',
          price: 30,
          vegan: false
        },
      }
    },
    template: `<div class="card" style="width: 18rem;">
      <div class="card-body" >
        <slot :product="product"></slot>
      </div>
    </div>`
  });

  app.component('card2', {
    props: ['product'],
    data() {
      return {
        veganName: ''
      }
    },
    created() {
      this.veganName = this.product.vegan ? '素食' : '非素食';
    },
    template: `<div class="card" style="width: 18rem;">
      <div class="card-body" >
        <slot :product="product" :veganName="veganName"></slot>
      </div>
    </div>`
  })

  app.mount('#app');
</script>

上一篇
Day23 : 路由守衛(下)
下一篇
Day 25 : $nextTick()
系列文
程式小白的 vue.js 學習筆記30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言